#pragma once

#include "../common/Log.hpp"
#include "../common/SqliteHelper.hpp"
#include "../common/FileHelper.hpp"
#include "../common/message.pb.h"
#include "Binding.hpp"
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <cassert>

namespace micromq
{
    // 一个交换机所绑定的队列信息<队列名, 交换机名->队列名>
    using BindingInfo = std::unordered_map<std::string, Binding::ptr>;

    // 所有交换机的绑定信息<交换机名, <队列名, 交换机名->队列名>>
    using BindingMap = std::unordered_map<std::string, BindingInfo>;

    class BindingMapper
    {
    public:
        BindingMapper(const std::string& dbFileName): _sqliteHelper(dbFileName)
        {
            std::string parentPath = FileHelper::getParentPath(dbFileName);
            FileHelper::createDir(parentPath);
            assert(_sqliteHelper.open());
            createTable();
        }

        void createTable()
        {
            std::stringstream sql;
            sql << "create table if not exists binding (          ";
            sql << "    exchange_name    varchar(32),             ";
            sql << "    msg_queue_name   varchar(32),             ";
            sql << "    binding_key      varchar(128),            ";
            sql << "    durable          int                      ";
            sql << ");                                            ";

            if(!_sqliteHelper.execute(sql.str(), nullptr, nullptr))
            {
                logError("create table binding error, abort...");
                ::abort();
            }
        }

        void removeTable()
        {
            std::string sql = "drop table if exists binding;";
            if(!_sqliteHelper.execute(sql, nullptr, nullptr))
            {
                logError("drop table binding error...");
            }
        }

        void insertBinding(Binding::ptr& binding)
        {
            std::stringstream sql;
            sql << "insert into binding values ('";
            sql << binding->_exchangeName << "', '";
            sql << binding->_msgQueueName << "', '";
            sql << binding->_bindingKey << "', ";
            sql << binding->_durable << "";
            sql << ");";

            if(!_sqliteHelper.execute(sql.str(), nullptr, nullptr))
            {
                logError("insert into binding error...");
            }
        }

        void deleteBindingByEntry(const std::string& ecxchangeName, const std::string& msgQueueName)
        {
            std::stringstream sql;
            sql << "delete from binding where ";
            sql << "exchange_name='" << ecxchangeName << "' and ";
            sql << "msg_queue_name='" << msgQueueName << "';";

            if(!_sqliteHelper.execute(sql.str(), nullptr, nullptr))
            {
                logError("delete binding error...");
            }
        }

        void deleteBindingByEcxchange(const std::string& ecxchangeName)
        {
            std::stringstream sql;
            sql << "delete from binding where ";
            sql << "exchange_name='" << ecxchangeName << "';";

            if(!_sqliteHelper.execute(sql.str(), nullptr, nullptr))
            {
                logError("delete binding error...");
            }
        }

        void deleteBindingByMsgQueue(const std::string& msgQueueName)
        {
            std::stringstream sql;
            sql << "delete from binding where ";
            sql << "msg_queue_name='" << msgQueueName << "';";

            if(!_sqliteHelper.execute(sql.str(), nullptr, nullptr))
            {
                logError("delete binding error...");
            }
        }

        BindingMap selectAll()
        {
            std::string sql = "select * from binding;";
            BindingMap bindingMap;
            if(!_sqliteHelper.execute(sql, onSelectAll, &bindingMap))
            {
                logError("select all from binding error...");
            }
            return bindingMap;
        }

        ~BindingMapper()
        {}
    private:
        static int onSelectAll(void* args, int colNum, char** result, char** fieldName)
        {
            auto bindingMap = static_cast<BindingMap*>(args);
            auto binding = std::make_shared<Binding>(result[0], result[1], result[2], result[3]);
            BindingInfo& bindingInfo = (*bindingMap)[binding->_exchangeName]; //从bindingMap获取防止覆盖历史数据，无则新建
            bindingInfo.insert(std::make_pair(binding->_msgQueueName, binding));
            return 0;
        }
    private:
        SqliteHelper _sqliteHelper;
    };
}